﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text.RegularExpressions;

namespace _5E.Utils.DbHelper.ParameterParser
{
    /// <summary>
    /// 程序说明：解析基类
    /// 创建作者：ahl5esoft
    /// 创建时间：2012-04-17
    /// </summary>
    /// <typeparam name="T">DbParameter</typeparam>
    public abstract class BaseParser<T> : IParameterParser<DbParameter> where T : DbParameter, new()
    {
        #region 变量

        private const string PARAMETER_FORMAT = "@param_{0}";

        private static Regex s_Match = new Regex(@":(\w+)");

        private Dictionary<string, Dictionary<string, T>> m_Cache = new Dictionary<string, Dictionary<string, T>>();

        private Int32 m_Index = 0;

        #endregion

        #region 实例方法

        public DbParameter[] Parse(ref string sql)
        {
            Reset();
            sql = ParseSQL(sql);
            var dbParameters = GetDbParameters();
            return dbParameters;
        }

        private void Reset()
        {
            m_Index = 0;
            m_Cache.Clear();
        }

        private string ParseSQL(string sql)
        {
            var newSql = s_Match.Replace(sql, match =>
            {
                string parameterName = match.Groups[1].Value, dbParameterStr = string.Empty;
                Dictionary<string, T> dbParameters;
                var existParameterName = m_Cache.TryGetValue(parameterName, out dbParameters);
                if (!existParameterName)
                {
                    var parameterValue = GetParameterValue(parameterName);
                    AddDbParameters(parameterName, parameterValue);
                }
                dbParameterStr = string.Join(",", m_Cache[parameterName].Keys.ToArray());
                return dbParameterStr;
            });
            return newSql;
        }

        protected abstract object GetParameterValue(string parameterName);

        private void AddDbParameters(string propertyName, object value)
        {
            var dbPrameters = new Dictionary<string, T>();
            if (value != null && value.GetType().IsArray)
            {
                foreach (var item in (value as Array))
                {
                    var dbParameterName = GetDbParameterName();
                    var dbParameter = CreateDbParameter(dbParameterName, item);
                    dbPrameters.Add(dbParameterName, dbParameter);
                }
            }
            else
            {
                var dbParameterName = GetDbParameterName();
                var dbParameter = CreateDbParameter(dbParameterName, value);
                dbPrameters.Add(dbParameterName, dbParameter);
            }
            m_Cache.Add(propertyName, dbPrameters);
        }

        private string GetDbParameterName()
        {
            return string.Format(PARAMETER_FORMAT, m_Index++);
        }

        private T CreateDbParameter(string parameterName, object parameterValue)
        {
            var parameter = new T();
            parameter.ParameterName = parameterName;
            parameter.Value = parameterValue == null ? DBNull.Value : parameterValue;
            return parameter;
        }

        private T[] GetDbParameters()
        {
            var parameters = new List<T>();
            foreach (var cachePair in m_Cache)
            {
                foreach (var parameterPair in cachePair.Value)
                {
                    parameters.Add(parameterPair.Value);
                }
            }
            return parameters.ToArray();
        }

        #endregion
    }
}
